package com.basketball.gym.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import jodd.util.Base64;
import lombok.Data;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Service;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Service
@Data
@ConfigurationProperties(prefix = "wx")
public class WXService {
    @Autowired
    private HttpService httpService;

    private String appId;
    private String appSecret;
    private String grantType;
    private String lang;
    private String mchId;
    private String notifyUrl;
    private String refundNotifyUrl;
    private String serialNo;
    private String keyFilePath;
    private String api3;


    /**
     * 获取微信网页的openId和token
     *
     * @param code:code
     * @return openid, access_token, refresh_token
     */
    public Map<String, Object> getOpenId(String code) {
        System.out.println(appId);
        System.out.println(appSecret);
        System.out.println(grantType);
//        loadData();
        String accessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
        Map<String, Object> param = new HashMap<>();
        param.put("appid", appId);
        param.put("secret", appSecret);
        param.put("code", code);
        param.put("grant_type", grantType);
        String result = httpService.httpGet(accessTokenUrl, param);
        System.out.println(result);
        return (Map<String, Object>) JSONObject.parse(result);
    }

    /**
     * 微信网页获取用户信息
     *
     * @param accessToken：token
     * @param openId：openid
     * @return user
     */
    public Map<String, Object> getUser(String accessToken, String openId) {
        String url = "https://api.weixin.qq.com/sns/userinfo";
        Map<String, Object> param = new HashMap<>();
        param.put("access_token", accessToken);
        param.put("openid", openId);
        param.put("lang", lang);
        String result = httpService.httpGet(url, param);
        Map<String, Object> res = (Map<String, Object>) JSONObject.parse(result);
        System.out.println(res);
        return res;
    }

    /**
     * 微信小程序获取openId,
     *
     * @param code:code
     * @return openid, session_key
     */
    public Map<String, Object> getMiNiOpenId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session";
        Map<String, Object> param = new HashMap<>();
        param.put("appid", appId);
        param.put("secret", appSecret);
        param.put("js_code", code);
        param.put("grant_type", grantType);
        String result = httpService.httpGet(url, param);
        Map<String, Object> res = (Map<String, Object>) JSONObject.parse(result);
        System.out.println(res);
        return res;
    }

    /**
     * 小程序获取信息
     */
    public Map<String, Object> getUserInfo(String encryptedData, String sessionkey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionkey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JSONObject.parseObject(result);
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidParameterSpecException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 发起支付
     *
     * @param description  ：商品描述
     * @param out_trade_no ：订单编号
     * @param total        ：价格（单位分）
     * @param openid       ：openid
     * @return prepay_id
     */
    public Map<String, Object> JSAPIPay(String description, String out_trade_no, int total, String openid) {
        String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
        Map<String, Object> param = new HashMap<>();
        param.put("appid", appId);
        param.put("mchid", mchId);
        param.put("description", description);
        param.put("out_trade_no", out_trade_no);
        param.put("notify_url", notifyUrl);
        Map<String, Object> amount = new HashMap<>();
        amount.put("total", total);
        amount.put("currency", "CNY");
        param.put("amount", amount);
        Map<String, Object> payer = new HashMap<>();
        payer.put("openid", openid);
        param.put("payer", payer);
        try {
            String json = JSON.toJSONString(param);
            System.out.println(json);
            String token = getToken("POST", "/v3/pay/transactions/jsapi", json);
            System.out.println(token);
            String result = CreateOrder(url, json, token);
            Map<String, Object> res = (Map<String, Object>) JSONObject.parse(result);
            System.out.println(res);
            String prepayId = (String) res.get("prepay_id");
            return getResponseData(prepayId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 退款
     *
     * @param transactionId：微信订单号
     * @param out_refund_no：商户退款号
     * @param out_trade_no：商户订单号
     * @param reason：退款原因
     * @param refund：退款金额
     * @param total：订单金额
     * @return
     */
    public Map<String, Object> refund(String transactionId, String out_refund_no, String out_trade_no, String reason, int refund, int total) {
        String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
        Map<String, Object> param = new HashMap<>();
//        param.put("transaction_id", transactionId);
//        param.put("reason",reason);
        param.put("out_refund_no", out_refund_no);
        param.put("out_trade_no",out_trade_no);
        param.put("notify_url", refundNotifyUrl);
        Map<String, Object> amount = new HashMap<>();
        amount.put("refund", refund);
        amount.put("total", total);
        amount.put("currency", "CNY");
        param.put("amount", amount);
        try {
            String json = JSON.toJSONString(param);
            System.out.println(json);
            String token = getToken("POST", "/v3/refund/domestic/refunds", json);
            System.out.println(token);
            String result = CreateOrder(url, json, token);
            Map<String, Object> res = (Map<String, Object>) JSONObject.parse(result);
            System.out.println(res);
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取返回前端的数据
     */
    private Map<String, Object> getResponseData(String prepayId) {
        long timestamp = System.currentTimeMillis() / 1000;
        String nonceStr = "433C3BDD53D816244943034900F16A592A34B5D1";
        String signature = getClientPaySign(timestamp, nonceStr, prepayId);
        Map<String, Object> data = new HashMap<>();
        data.put("appId", appId);
        data.put("timeStamp", timestamp);
        data.put("nonceStr", nonceStr);
        data.put("package", "prepay_id=" + prepayId);
        data.put("signType", "RSA");
        data.put("paySign", signature);
        return data;
    }

    /**
     * 发起请求
     */
    public String CreateOrder(String url, String reqdata, String token) throws Exception {
        CloseableHttpClient httpClient = httpService.getConnection();
        //请求URL
        HttpPost httpPost = new HttpPost(url);
        StringEntity entity = new StringEntity(reqdata);
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Authorization", token);

        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
//                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
                return EntityUtils.toString(response.getEntity());
            } else if (statusCode == 204) {
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
        }
        return null;
    }


    /**
     * Authorization前缀
     */
    String schema = "WECHATPAY2-SHA256-RSA2048";

    /**
     * 获取Authorization
     *
     * @param method：请求方法
     * @param url：url
     * @param body：请求体
     * @return token
     */
    public String getToken(String method, String url, String body) {
        try {
            String nonceStr = "433C3BDD53D816244943034900F16A592A34B5D1";
            long timestamp = System.currentTimeMillis() / 1000;
            String message = buildMessage(method, url, timestamp, nonceStr, body);
            System.out.println("message" + message);
            String signature = sign(message.getBytes("utf-8"));

            return schema + " " + "mchid=\"" + mchId + "\","
                    + "nonce_str=\"" + nonceStr + "\","
                    + "timestamp=\"" + timestamp + "\","
                    + "serial_no=\"" + serialNo + "\","
                    + "signature=\"" + signature + "\"";
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getClientPaySign(long timestamp, String nonceStr, String prepayId) {
        try {
            String message = buildClientMessage(timestamp, nonceStr, prepayId);
            System.out.println(message);
            return sign(message.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获得签名
     *
     * @param message
     * @return
     */
    String sign(byte[] message) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(getPrivateKey());
            System.out.println("bytemess" + message);
            sign.update(message);
            return java.util.Base64.getEncoder().encodeToString(sign.sign());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SignatureException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 后端获取签名的信息
     *
     * @param method：请求方法（需要大写）
     * @param url：请求地址
     * @param timestamp：时间戳
     * @param nonceStr：随机字符串
     * @param body：请求体（get求情为null)
     * @return String
     */
    String buildMessage(String method, String url, long timestamp, String nonceStr, String body) {
        return method + "\n"
                + url + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    String buildClientMessage(long timestamp, String nonceStr, String prepayId) {
        return appId + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + "prepay_id=" + prepayId + "\n";
    }

    /**
     * 获取私钥。
     * <p>
     * //     * @param filename 私钥文件路径  (required)
     *
     * @return 私钥对象
     */
    public PrivateKey getPrivateKey() throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(keyFilePath)), "utf-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(java.util.Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

    /**
     * 解密支付回调
     */
    public Map<String, Object> decrypt(byte[] associatedData, byte[] nonce, String ciphertext) throws IOException, GeneralSecurityException {
        AesUtil aesUtil = new AesUtil(api3.getBytes("UTF-8"));
        String result = aesUtil.decryptToString(associatedData, nonce, ciphertext);
        Map<String, Object> res = (Map<String, Object>) JSONObject.parse(result);
        return res;
    }

}
